<?php

namespace app\api\controller;

use app\api\library\Err;
use think\Request;
use app\common\controller\ApiController;
use think\Db;
use app\common\library\RedisReentrantLock;


/**
 * 预约接口
 */
class Appointment extends ApiController
{
    protected $noNeedLogin = [];
    protected $noNeedRight = '*';

    // 预约状态：0-未使用，1-已使用，2-已取消
    const STATUS_UNUSED = 0;
    const STATUS_USED = 1;
    const STATUS_CANCELED = 2;

    const STATUS_HIDE = 0;
    const STATUS_SHOW = 1;

    // status 待入场=0，已入场=1，已过期=2
    const STATUS_BEFORE_JOIN = 0;
    const STATUS_JOINED = 1;
    const STATUS_EXPIRE = 2;

    protected $tableName = 'appointment';
    protected $fields = '';
    protected $fieldsExcept = false;
    protected $onlySelf = true;
    protected $orderBy = 'id desc';
    // deletetime 为null
    protected $conditions = ['deletetime' => null];

    protected function processData($method, $data)
    {
        if ($method === 'beforeSave') {
            // 创建可重入锁
            $lock = new RedisReentrantLock('order_lock');

            // 获取锁（可重入）
            if ($lock->lock(10)) {
                try {
                    $userId = $this->auth->getUser()->id;

                    // 检查用户是否已经预约过该场次
                    $appointment = Db::name('appointment')->where('scene_id', $data['scene_id'])
                        ->where('user_id', $userId)
                        ->where('deletetime', null)
                        ->find();
                    if ($appointment) {
                        $this->fail("您已预约过该场次", 400, Err::COMMON_ERR);
                    }

                    // 检查场次是否存在
                    $scene = Db::name('scene')->where('id', $data['scene_id'])->find();
                    if (!$scene) {
                        $this->fail("场次不存在", 404, Err::COMMON_ERR);
                    }

                    // 检查场次是否已满
                    if ($scene['current_people'] >= $scene['max_people']) {
                        $this->fail("该场次预约已满", 400, Err::COMMON_ERR);
                    }
                    // 该场次是否结束
                    if (time() >= strtotime($scene['date'] . ' ' . $scene['endtime'])) {
                        $this->fail("该场次已结束不能预约", 400, Err::COMMON_ERR);
                    }
                    // 该场次是否已经可以开始预约
                    $tomorrow = date('Y-m-d', strtotime('+1 day'));
                    if ($scene['date'] > $tomorrow) {
                        $this->fail("该场次未开启预约", 400, Err::COMMON_ERR);
                    }

                    // 该场次当天是否有其他场次预约（每个用户每天只能约一场）
                    $appointment = Db::name('appointment')->where('date', $scene['date'])
                        ->where('user_id', $userId)
                        ->where('deletetime', null)
                        ->find();
                    if ($appointment) {
                        $this->fail("您已预约过同一天的场次", 400, Err::COMMON_ERR);
                    }

                    // 检查场次状态
                    // if ($scene['status'] != self::STATUS_SHOW) {
                    //     $this->fail("该场次不可预约", 400, Err::COMMON_ERR);
                    // }

                    // 生成预约号（随机七位数字与用户idID拼接）
                    $appointmentNo = $userId . mt_rand(1000000, 9999999);
                    $data['appointment_no'] = $appointmentNo;
                    // 活码5分钟过期
                    $data['appointment_no_expiration_time'] = time() + 5 * 60;
                    // 更新场次当前预约人数
                    $count = Db::name('scene')
                        ->where('id', $data['scene_id'])
                        ->where('current_people', '<', $scene['max_people'])
                        ->setInc('current_people');

                    if (!$count) {
                        $this->fail("预约失败，请稍后再试", 400, Err::COMMON_ERR);
                    }
                    $data['appointment_time'] = time();
                    $data['createtime'] = time();
                    $data['user_id'] = $userId;
                    $data['date'] = $scene['date'];
                    $data['starttime'] = strtotime($scene['date'] . ' ' . $scene['starttime']);
                    $data['endtime'] = strtotime($scene['date'] . ' ' . $scene['endtime']);
                    return $data;
                } catch (\think\exception\ErrorException $e) {
                    $msg = $e->getMessage();
                    $file = $e->getFile();
                    $line = $e->getLine();
                    trace("msg: $msg | file: $file | line: $line", 'error');
                    $this->fail(__('Failed to update data'));
                } finally {
                    // 释放锁
                    $lock->unlock();
                }
            } else {
                $this->fail("服务器繁忙，请稍后再试", 400, Err::COMMON_ERR);
            }
        } else if ($method === 'afterSave') {
            // 预约成功后的处理
            return $data;
        } else if ($method === 'afterList') {
            // 列表数据处理
            foreach ($data as $key => $row) {
                if ($row['is_used'] == 0) {
                    if (date('Y-m-d') > $row['date']) {
                        $data[$key]['status'] = self::STATUS_EXPIRE;
                    } else {
                        $data[$key]['status'] = self::STATUS_BEFORE_JOIN;
                    }
                } else {
                    $data[$key]['status'] = self::STATUS_JOINED;
                }
                $data[$key]['date'] = date('Y-m-d', $row['starttime']);
                $data[$key]['starttime'] = date('H:i', $row['starttime']);
            }
            return $data;
        } else if ($method === 'beforeList') {
            // 列表查询前处理
            return $data;
        }

        return $data;
    }

    /**
     * 保存新建的资源
     *
     * @param  \think\Request  $request
     * @return \think\Response
     */
    public function save(Request $request)
    {
        $data = $this->request->param('', null, $this->paramFilter);
        Db::startTrans();

        try {
            $data = $this->processData('beforeSave', $data);
        } catch (\think\exception\ErrorException $e) {
            $msg = $e->getMessage();
            $file = $e->getFile();
            $line = $e->getLine();
            trace("msg: $msg | file: $file | line: $line", 'error');
            Db::rollback();
            $this->fail(__('Failed to update data'));
        }

        if ($this->onlySelf) {
            $user = $this->auth->getUser();
            $data['user_id'] = $user->id;
        }

        try {
            $id = Db::name($this->tableName)->insertGetId($data);
            if (!$id) {
                $this->fail(__('Failed to update data'));
            }
            $data['id'] = $id;
            $data = $this->processData('afterSave', $data);
            Db::commit();
        } catch (\think\exception\ErrorException $e) {
            $msg = $e->getMessage();
            $file = $e->getFile();
            $line = $e->getLine();
            Db::rollback();
            trace("msg: $msg | file: $file | line: $line", 'error');
            $this->fail(__('Failed to update data'));
        } catch (\think\Exception $e) {
            $msg = $e->getMessage();
            trace("msg: $msg", 'error');
            Db::rollback();
            $this->fail(__('Failed to update data'));
        }

        return $this->success('', $data);
    }

    /**
     * 获取我的预约列表
     *
     * @return \think\Response
     */
    public function myAppointments()
    {
        $userId = $this->auth->getUser()->id;
        $status = $this->request->get('status', null);

        $query = Db::name($this->tableName)
            ->alias('a')
            ->join('scene s', 'a.scene_id = s.id')
            ->where('a.user_id', $userId)
            ->where('a.deletetime', null);

        // 根据状态筛选
        if ($status !== null) {
            $query = $query->where('s.status', $status);
        }

        list($start, $limit) = $this->paging();

        $data = $query->field('a.*, s.date, s.starttime, s.endtime, s.status as scene_status')
            ->order('a.id desc')
            ->limit($start, $limit)
            ->select();

        return $this->success('', $data);
    }

    /**
     * 获取预约详情
     *
     * @param int $id 预约ID
     * @return \think\Response
     */
    public function detail($id)
    {
        $userId = $this->auth->getUser()->id;

        $data = Db::name($this->tableName)
            ->alias('a')
            ->join('scene s', 'a.scene_id = s.id')
            ->where('a.id', $id)
            ->where('a.user_id', $userId)
            ->where('a.deletetime', null)
            ->field('a.*, s.date, s.starttime, s.endtime, s.max_people, s.current_people, s.status as scene_status')
            ->find();

        if (!$data) {
            $this->fail("预约记录不存在", 404, Err::COMMON_ERR);
        }

        // 添加分享信息
        $data['share_info'] = [
            'title' => '我预约了「威神V快闪」活动',
            'desc' => '预约时间：' . date('Y-m-d', strtotime($data['date'])) . ' ' . $data['starttime'] . '-' . $data['endtime'],
            'link' => request()->domain() . '/pages/appointment/detail?id=' . $id,
            'imgUrl' => request()->domain() . '/assets/img/share_logo.png'
        ];

        return $this->success('', $data);
    }

    /**
     * 获取预约分享信息
     *
     * @param int $id 预约ID
     * @return \think\Response
     */
    public function shareInfo($id)
    {
        $userId = $this->auth->getUser()->id;

        $appointment = Db::name($this->tableName)
            ->alias('a')
            ->join('scene s', 'a.scene_id = s.id')
            ->where('a.id', $id)
            ->where('a.user_id', $userId)
            ->where('a.deletetime', null)
            ->field('a.id, a.appointment_no, s.date, s.starttime, s.endtime')
            ->find();

        if (!$appointment) {
            $this->fail("预约记录不存在", 404, Err::COMMON_ERR);
        }

        $shareInfo = [
            'title' => '我预约了「威神V快闪」活动',
            'desc' => '预约时间：' . date('Y-m-d', strtotime($appointment['date'])) . ' ' . $appointment['starttime'] . '-' . $appointment['endtime'],
            'link' => request()->domain() . '/pages/appointment/detail?id=' . $id,
            'imgUrl' => request()->domain() . '/assets/img/share_logo.png'
        ];

        return $this->success('', $shareInfo);
    }

    /**
     * 核销预约码
     *
     * @param string $code 预约码
     * @return \think\Response
     */
    public function verify()
    {
        // 只允许核销员或管理员核销
        if (!$this->isAdminOrHexiaoyuan()) {
            $this->fail("您没有权限进行此操作", 403, Err::UNAUTHENTICATED);
        }

        $code = $this->request->post('appointment_no', '', 'trim');
        if (empty($code)) {
            $this->fail("预约码不能为空", 400, Err::INVALID_PARAM);
        }

        // 创建可重入锁
        $lock = new RedisReentrantLock('verify_lock_' . $code);

        // 获取锁（可重入）
        if ($lock->lock(10)) {
            try {
                // 查找预约记录
                $appointment = Db::name($this->tableName)
                    ->where('appointment_no', $code)
                    // ->where('is_used', self::STATUS_UNUSED)
                    ->where('deletetime', null)
                    ->find();

                if (!$appointment) {
                    $this->fail("预约码失效，请重新获取", 400, Err::COMMON_ERR);
                }

                if ($appointment['is_used'] == self::STATUS_USED) {
                    $this->fail("预约码已使用", 400, Err::COMMON_ERR);
                }

                if (time() > $appointment['appointment_no_expiration_time']) {
                    $this->fail("预约码已过期，请重新获取", 400, Err::COMMON_ERR);
                }

                // 查找场次信息
                $scene = Db::name('scene')->where('id', $appointment['scene_id'])->find();
                if (!$scene) {
                    $this->fail("场次不存在", 404, Err::COMMON_ERR);
                }

                // 开始15分钟后不能核销
                // if (time() > strtotime($scene['date'] . ' ' . $scene['starttime']) + 15 * 60) {
                //     $this->fail("已经开场15分钟不能核销", 400, Err::COMMON_ERR);
                // }

                // 只能核销当天的场次
                if ($appointment['date'] != date('Y-m-d')) {
                    $this->fail("只能核销当天的场次", 400, Err::COMMON_ERR);
                }

                Db::startTrans();
                try {
                    // 更新预约状态为已使用
                    $result = Db::name($this->tableName)
                        ->where('id', $appointment['id'])
                        ->update([
                            'usetime' => time(),
                            'is_used' => self::STATUS_USED,
                            'operator_id' => $this->auth->getUser()->id,
                            'updatetime' => time()
                        ]);

                    if (!$result) {
                        Db::rollback();
                        $this->fail("核销失败，请稍后再试", 400, Err::COMMON_ERR);
                    }

                    Db::commit();

                    // 获取用户信息和场次信息，返回详细数据
                    $verifyInfo = Db::name($this->tableName)
                        ->alias('a')
                        ->join('user u', 'a.user_id = u.id')
                        ->join('scene s', 'a.scene_id = s.id')
                        ->where('a.id', $appointment['id'])
                        ->field('a.id, a.appointment_no, a.usetime, u.nickname, u.avatar, s.date, s.starttime, s.endtime')
                        ->find();

                    return $this->success("核销成功", $verifyInfo);
                } catch (\think\Exception $e) {
                    Db::rollback();
                    $this->fail("核销失败: " . $e->getMessage(), 400, Err::COMMON_ERR);
                }
            } catch (\think\exception\ErrorException $e) {
                $this->fail("核销失败: " . $e->getMessage(), 400, Err::COMMON_ERR);
            } finally {
                // 释放锁
                $lock->unlock();
            }
        } else {
            $this->fail("服务器繁忙，请稍后再试", 400, Err::COMMON_ERR);
        }
    }

    /**
     * 查询预约码状态
     *
     * @param string $code 预约码
     * @return \think\Response
     */
    public function checkCode()
    {
        // 只允许管理员查询
        if (!$this->auth->getUser()->isAdmin()) {
            $this->fail("您没有权限进行此操作", 403, Err::UNAUTHENTICATED);
        }

        $code = $this->request->get('code', '', 'trim');
        if (empty($code)) {
            $this->fail("预约码不能为空", 400, Err::INVALID_PARAM);
        }

        // 查找预约记录
        $appointment = Db::name($this->tableName)
            ->alias('a')
            ->join('user u', 'a.user_id = u.id')
            ->join('scene s', 'a.scene_id = s.id')
            ->where('a.appointment_no', $code)
            ->where('a.deletetime', null)
            ->field('a.id, a.appointment_no, a.status, a.verify_time, a.verify_admin_id, u.nickname, u.avatar, s.date, s.starttime, s.endtime')
            ->find();

        if (!$appointment) {
            $this->fail("预约码不存在", 404, Err::COMMON_ERR);
        }

        // 获取核销管理员信息
        if ($appointment['verify_admin_id']) {
            $admin = Db::name('admin')->where('id', $appointment['verify_admin_id'])->field('id, username, nickname')->find();
            $appointment['verify_admin'] = $admin ?: null;
        }

        // 格式化状态信息
        $statusText = '';
        switch ($appointment['status']) {
            case self::STATUS_UNUSED:
                $statusText = '未使用';
                break;
            case self::STATUS_USED:
                $statusText = '已使用';
                break;
            case self::STATUS_CANCELED:
                $statusText = '已取消';
                break;
            default:
                $statusText = '未知状态';
        }
        $appointment['status_text'] = $statusText;

        return $this->success('', $appointment);
    }

    private function isAdminOrHexiaoyuan()
    {
        $groupId = $this->auth->getUserinfo()['group_id'];
        return $groupId == 2 || $groupId == 3;
    }

    /**
     * 
     * 刷新预约二维码
     * @param string $code 预约码
     * @return \think\Response
     * @return void
     */
    public function refreshno()
    {
        $id = $this->request->post('id', '', 'trim');
        $userId = $this->auth->getUser()->id;
        $appointment = Db::name('appointment')->where('id', $id)
            ->where('user_id', $userId)
            ->where('deletetime', null)
            ->find();
        if (!$appointment) {
            $this->fail("该预约不存在", 404, Err::COMMON_ERR);
        }
        if ($appointment['is_used']) {
            $this->fail("该预约码已核销", 400, Err::COMMON_ERR);
        }
        // 生成新的预约码
        $appointmentNo = $userId . mt_rand(1000000, 9999999);


        $data = [
            'appointment_no' => $appointmentNo,
            'appointment_no_expiration_time' => time() + 5 * 60,
            'updatetime' => time()
        ];

        $result = Db::name($this->tableName)
            ->where('id', $appointment['id'])
            ->update($data);
        if (!$result) {
            $this->fail("获取失败，请重试", 400, Err::COMMON_ERR);
        }
        $appointment = array_merge($appointment, $data);

        return $this->success('', $appointment);
    }
}
